package main

import (
	"context"
	"flag"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/spf13/viper"
	"github.com/tianjigames/fairy/constants"
	"github.com/tianjigames/fairy/dao"
	"github.com/tianjigames/fairy/manager"
	protos2 "github.com/tianjigames/fairy/protos"
	"github.com/tianjigames/fairy/services/game"
	"github.com/tianjigames/fairy/services/hall"
	"github.com/tianjigames/fairy/services/player"
	"github.com/tianjigames/fairy/services/world"
	"github.com/tianjigames/fairy/template"
	"github.com/topfreegames/pitaya"
	"github.com/topfreegames/pitaya/acceptor"
	"github.com/topfreegames/pitaya/cluster"
	"github.com/topfreegames/pitaya/component"
	"github.com/topfreegames/pitaya/logger"
	"github.com/topfreegames/pitaya/modules"
	"github.com/topfreegames/pitaya/route"
	"github.com/topfreegames/pitaya/serialize/protobuf"
	"github.com/topfreegames/pitaya/session"
	"strings"
)



func configureFrontend(port int) error {
	tcp := acceptor.NewTCPAcceptor(fmt.Sprintf(":%d", port))
	err := pitaya.AddRoute("room", func(
		ctx context.Context,
		route *route.Route,
		payload []byte,
		servers map[string]*cluster.Server,
	) (*cluster.Server, error) {
		// will return the first server
		for k := range servers {
			return servers[k], nil
		}
		return nil, nil
	})

	if err != nil {
		logger.Log.Errorf("error adding route %s\n", err.Error())
		return err
	}

	err = pitaya.SetDictionary(map[string]uint16{
		"connector.getsessiondata": 1,
		"connector.setsessiondata": 2,
		"room.room.getsessiondata": 3,
		"onMessage":                4,
		"onMembers":                5,
	})

	if err != nil {
		logger.Log.Errorf("error setting route dictionary %s\n", err.Error())
		return err
	}

	pitaya.AddAcceptor(tcp)

	//session创建时，从Player服务获取一个唯一的sessionId进行绑定
	session.OnSessionCreate(func(s *session.Session) error {
		ctx := context.Background()
		reply := &protos2.GatePOnSessionCreatedRet{}
		err = pitaya.RPC(ctx, constants.GatePOnSessionCreatedRoute,reply,&protos2.GatePOnSessionCreated{})
		if err != nil {
			logger.Log.Errorf("session OnSessionCreate failed,error=%s",err.Error())
			return err
		}

		err = s.Bind(ctx,reply.SessionId)
		if err != nil {
			logger.Log.Errorf("session OnSessionCreate failed,error=%s",err.Error())
			return err
		}

		return nil
	})

	//session关闭时，给player服务推送一条消息 服务根据玩家在登录服 游戏服 做对应的处理
	session.OnSessionClose(func(s *session.Session) {
		reply := &protos2.GatePOnSessionClosedRet{}
		err1 := pitaya.RPC(context.Background(), constants.GatePOnSessionClosedRoute,reply,&protos2.GatePOnSessionClosed{
			SessionIds: []string{s.UID()},
		})
		if err1 != nil {
			logger.Log.Errorf("OnSessionClose failed,error=%s",err1.Error())
			return
		}
	})
	return nil
}

func configureHall() error {
	//注册模板文件
	pitaya.RegisterTemplate(template.GMAccountKey)
	pitaya.RegisterTemplate(template.StrFilterKey)

	//注册dao
	pitaya.RegisterDao(dao.NewGuidDataDao())
	pitaya.RegisterDao(dao.NewGuidGenertorDao())
	pitaya.RegisterDao(dao.NewAccountDataDao())
	pitaya.RegisterDao(dao.NewPlayerDataDao())
	pitaya.RegisterDao(dao.NewGlobalDataDao())
	pitaya.RegisterDao(dao.NewPlayerMailBoxDao())

	//注册管理器
	pitaya.RegisterManger(manager.NewPlayerManager())
	pitaya.RegisterManger(manager.NewGuiGeneratorManager())
	pitaya.RegisterManger(manager.NewLoginManger())
	pitaya.RegisterManger(manager.NewPlayerMailBoxManager())
	pitaya.RegisterManger(manager.NewGMAccountManager())

	loginHandler := hall.NewLoginHandler()
	pitaya.Register(loginHandler,
		component.WithName("login"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(loginHandler,
		component.WithName("login"),
		component.WithNameFunc(strings.ToLower),
	)

	roleHandler := hall.NewRoleHandler()
	pitaya.Register(roleHandler,
		component.WithName("role"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(roleHandler,
		component.WithName("role"),
		component.WithNameFunc(strings.ToLower),
	)

	gameHandler := hall.NewGameHandler()
	pitaya.Register(gameHandler,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gameHandler,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)

	//处理session关闭
	gateHandler := hall.NewGateHandler()
	pitaya.Register(gateHandler,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gateHandler,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)



	//注册线程
	loginThread := manager.NewLoginThread()
	pitaya.RegisterThread(loginThread,
		component.WithName(constants.LoginThread),
		component.WithNameFunc(strings.ToLower))
	return nil
}



func configureGame() error {
	//注册模板文件
	pitaya.RegisterTemplate(template.AttrFactorKey)
	pitaya.RegisterTemplate(template.FabaoBaseKey)
	pitaya.RegisterTemplate(template.GMAccountKey)
	pitaya.RegisterTemplate(template.ItemKey)
	pitaya.RegisterTemplate(template.MonsterEventKey)
	pitaya.RegisterTemplate(template.RideBaseKey)
	pitaya.RegisterTemplate(template.SceneBaseKey)
	pitaya.RegisterTemplate(template.SkillBaseKey)
	pitaya.RegisterTemplate(template.ThreeYbDataKey)
	pitaya.RegisterTemplate(template.GemKey)
	pitaya.RegisterTemplate(template.FabaoSkillKey)
	pitaya.RegisterTemplate(template.EquipKey)
	pitaya.RegisterTemplate(template.StrengthLevelKey)
	pitaya.RegisterTemplate(template.EquiplayKey)
	pitaya.RegisterTemplate(template.EquipStarKey)
	pitaya.RegisterTemplate(template.WingBaseKey)
	pitaya.RegisterTemplate(template.WingShowKey)
	pitaya.RegisterTemplate(template.TitleDefineKey)
	pitaya.RegisterTemplate(template.TreasureKey)
	pitaya.RegisterTemplate(template.FashionKey)
	pitaya.RegisterTemplate(template.MissionBaseKey)

	//注册dao
	pitaya.RegisterDao(dao.NewPlayerDataDao())
	pitaya.RegisterDao(dao.NewWeddingInvitationDataDao())
	pitaya.RegisterDao(dao.NewItemDao())
	pitaya.RegisterDao(dao.NewTreasureDao())
	pitaya.RegisterDao(dao.NewGuidGenertorDao())
	pitaya.RegisterDao(dao.NewFabaoDataDao())
	pitaya.RegisterDao(dao.NewRideDataDao())

	//注册管理器
	pitaya.RegisterManger(manager.NewTimeManager())
	pitaya.RegisterManger(manager.NewSpecialEventManager())
	pitaya.RegisterManger(manager.NewMapDataManager())
	pitaya.RegisterManger(manager.NewSceneManager())
	pitaya.RegisterManger(manager.NewPlayerManager())
	pitaya.RegisterManger(manager.NewTeamInfoManager())
	pitaya.RegisterManger(manager.NewLoadPlayerInfoManager())
	pitaya.RegisterManger(manager.NewSkillLogicManager())
	pitaya.RegisterManger(manager.NewItemManager())
	pitaya.RegisterManger(manager.NewTitleManager())
	pitaya.RegisterManger(manager.NewTreasureManager())
	pitaya.RegisterManger(manager.NewFashionManager())
	pitaya.RegisterManger(manager.NewMissionManager())
	pitaya.RegisterManger(manager.NewWeddingManager())
	pitaya.RegisterManger(manager.NewGuiGeneratorManager())
	pitaya.RegisterManger(manager.NewFabaoManager())
	pitaya.RegisterManger(manager.NewRideManager())
	pitaya.RegisterManger(manager.NewGangManager())

	//游戏处理
	gameHandler := game.NewGameHandler()
	pitaya.Register(gameHandler,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gameHandler,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)

	//组队处理
	teamHandler := game.NewTeamHandler()
	pitaya.Register(teamHandler,
		component.WithName("team"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(teamHandler,
		component.WithName("team"),
		component.WithNameFunc(strings.ToLower),
	)


	//注册协程
	daemonThread := manager.NewDaemonThread()
	pitaya.RegisterThread(daemonThread,
		component.WithName(constants.DaemonThread),
		component.WithNameFunc(strings.ToLower))
	incomingThread := manager.NewIncomingThread()
	pitaya.RegisterThread(incomingThread,
		component.WithName(constants.IncomingThread),
		component.WithNameFunc(strings.ToLower))

	return nil
}

func configureWorld() error {


	hallHandler := world.NewHallHandler()
	pitaya.Register(hallHandler,
		component.WithName("hall"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(hallHandler,
		component.WithName("hall"),
		component.WithNameFunc(strings.ToLower),
	)

	gameHandler := world.NewGameHandler()
	pitaya.Register(gameHandler,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gameHandler,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)

	gateHandler := world.NewGateHandler()
	pitaya.Register(gateHandler,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gateHandler,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)

	rankHandler := world.NewRankHandler()
	pitaya.Register(rankHandler,
		component.WithName("rank"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(rankHandler,
		component.WithName("rank"),
		component.WithNameFunc(strings.ToLower),
	)

	//注册dao
	pitaya.RegisterDao(dao.NewPlayerDataDao())
	pitaya.RegisterDao(dao.NewStandDataDao())
	pitaya.RegisterDao(dao.NewGangMemberDataDao())
	pitaya.RegisterDao(dao.NewGangDataDao())
	pitaya.RegisterDao(dao.NewExecuteMailDao())


	//注册管理器
	pitaya.RegisterManger(manager.NewPlayerManager())
	pitaya.RegisterManger(manager.NewPlayerNameManager())
	pitaya.RegisterManger(manager.NewGameServerPlayerManager())
	pitaya.RegisterManger(manager.NewTeamManager())
	pitaya.RegisterManger(manager.NewGangServerManager())
	pitaya.RegisterManger(manager.NewStandManager())
	pitaya.RegisterManger(manager.NewMailManager())


	return nil
}

/**
玩家管理服务
 */
func configurePlayer() error {
	playerHandler := player.NewPlayerHandler()
	pitaya.Register(playerHandler,
		component.WithName("player"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(playerHandler,
		component.WithName("player"),
		component.WithNameFunc(strings.ToLower),
	)

	//注册管理器
	pitaya.RegisterManger(manager.NewOnlinePlayerManager())
	return nil
}

func main() {
	port := flag.Int("port", 3250, "the port to listen")
	svType := flag.String("type", "connector", "the server type")
	isFrontend := flag.Bool("frontend", true, "if server is frontend")
	//rpcServerPort := flag.String("rpcsvport", "4222", "the port that grpc server will listen")

	flag.Parse()

	defer pitaya.Shutdown()

	pitaya.SetSerializer(protobuf.NewSerializer())
	confs := viper.New()
	//confs.Set("pitaya.cluster.rpc.server.grpc.port", *rpcServerPort)

	meta := map[string]string{
		//constants.GRPCHostKey: "127.0.0.1",
		//constants.GRPCPortKey: *rpcServerPort,
	}
	pitaya.Configure(*isFrontend, *svType, pitaya.Cluster, meta, confs)

	bs := modules.NewETCDBindingStorage(pitaya.GetServer(), pitaya.GetConfig())
	pitaya.RegisterModule(bs, "bindingsStorage")

	//注册数据库
	ds,err1 := modules.NewDatabaseStorage(pitaya.GetConfig())
	if err1 != nil {
		panic(err1)
	}
	pitaya.RegisterModule(ds,"databaseStorage")

	//注册redis
	rs := modules.NewRedisStorage(pitaya.GetConfig())
	pitaya.RegisterModule(rs,"redisStorage")

	var err error
	switch *svType {
	case constants.SvTypeConnector:
		err = configureFrontend(*port)
	case constants.SvTypeHall:
		err = configureHall()
	case constants.SvTypeGame:
		err = configureGame()
	case constants.SvTypeWorld:
		err = configureWorld()
	case constants.SvTypePlayer:
		err = configurePlayer()
	default:
		logger.Log.Errorf("error svType %s\n", *svType)
		return
	}

	if err != nil{
		panic(err)
	}

	ns, err := cluster.NewNatsRPCServer(pitaya.GetConfig(), pitaya.GetServer(), pitaya.GetMetricsReporters(), pitaya.GetDieChan())
	if err != nil {
		panic(err)
	}

	nc, err := cluster.NewNatsRPCClient(
		pitaya.GetConfig(),
		pitaya.GetServer(),
		pitaya.GetMetricsReporters(),
		pitaya.GetDieChan(),
	)
	if err != nil {
		panic(err)
	}
	pitaya.SetRPCServer(ns)
	pitaya.SetRPCClient(nc)
	pitaya.Start()
	//protoc -I=. -I=../ -I=E:/learn/go_project/src --gogo_out=../ login.proto
	//protoc ./proto/inner_message_common.proto ./proto/inner_message_lw_msg.proto --go_out=./
}